package com.hxzy.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hxzy.common.annon.StopWatch;
import com.hxzy.common.constans.RedisConstans;
import com.hxzy.common.enums.AckCode;
import com.hxzy.dto.SysDictDataSearchDTO;
import com.hxzy.entity.SysDictData;
import com.hxzy.exception.ServiceException;
import com.hxzy.mapper.SysDictDataMapper;
import com.hxzy.service.SysDictDataService;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 字典明细
 */
@Log4j2
@Service
public class SysDictDataServiceImpl extends ServiceImpl<SysDictDataMapper, SysDictData> implements SysDictDataService {

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 根据类型查询值
     * @param dictType
     * @return
     */
    @Override
    public List<SysDictData> findByDictType(String dictType) {
        //先判断hash中是否存在这外键，如果存在执行后面操作，如果不存在，可能是缓存穿透的问题
        //可能存在缓存穿透的情况
        if(!this.redisTemplate.opsForHash().hasKey(RedisConstans.DICT_DATA_HASH_KEY,dictType)){
            log.error("sys_dict_data表查询，键="+dictType+",并恶意攻击了");
            throw new ServiceException(AckCode.SYSTEM_PARAM_FAIL);
        }

        List<SysDictData> sysDictDataList=null;
        String redisKey= RedisConstans.getRedisKey(RedisConstans.DICT_DATA_KEY,dictType);
        Object redisValue=this.redisTemplate.opsForValue().get(redisKey);
        if(redisValue==null){
            // 查询数据库，加入缓存中
            sysDictDataList = this.loadDictDataByDictTypeToRedis(dictType);
        }else{
            // 得到缓存
            sysDictDataList= (List<SysDictData>) redisValue;
        }
        return sysDictDataList;
    }

    /**
     * 加载所有的sys_dict_data数据到redis缓存中
     */
    @StopWatch
    @Override
    public void loadAllDictDataToRedis() {
        List<SysDictData> allDictData = this.list();

        Map<String, List<SysDictData>> collect = allDictData.stream().collect(Collectors.groupingBy(SysDictData::getDictType));

        for(String key : collect.keySet()){
            String redisKey= RedisConstans.getRedisKey(RedisConstans.DICT_DATA_KEY,key);
            List<SysDictData> redisValue=collect.get(key);
            this.redisTemplate.opsForValue().set(redisKey,redisValue);

            //使用 hash来存储所有的key，为了解决redis单击被击穿的问题
            this.redisTemplate.opsForHash().put(RedisConstans.DICT_DATA_HASH_KEY,key,1);
        }
        // 释放资源
        collect=null;
        allDictData=null;
    }

    /**
     * 清空所有redis中存放到sys_dict_data数据
     */
    @StopWatch
    @Override
    public void clearDictDataToRedis() {
        Set keys = this.redisTemplate.keys(RedisConstans.DICT_DATA_KEY + "*");
        if(keys!=null && keys.size()>0){
            this.redisTemplate.delete(keys);
        }

        this.redisTemplate.delete(RedisConstans.DICT_DATA_HASH_KEY);

    }

    /**
     * 根据dict_type删除redis中缓存
     * @param dictType
     */
    @Override
    public void clearDictDataToRedisByDictType(String dictType) {
       String redisKey= RedisConstans.getRedisKey(RedisConstans.DICT_DATA_KEY,dictType);
       this.redisTemplate.delete(redisKey);

       //hash中相应的值
        this.redisTemplate.opsForHash().get(RedisConstans.DICT_DATA_HASH_KEY, dictType);
    }

    /**
     * 根据dict_type查询出来写入到redis中
     * @param dictType
     */
    @Override
    public List<SysDictData> loadDictDataByDictTypeToRedis(String dictType) {
        String redisKey= RedisConstans.getRedisKey(RedisConstans.DICT_DATA_KEY,dictType);
        List<SysDictData> dictDataList = this.findByDictType(redisKey);

        this.redisTemplate.opsForValue().set(redisKey,dictDataList);

        //写入到hash
        this.redisTemplate.opsForHash().put(RedisConstans.DICT_DATA_HASH_KEY,dictType,1);
        return dictDataList;
    }

    /**
     * 分页查询
     * @param dto
     * @return
     */
    @Override
    public IPage<SysDictData> findByDictTypePage(SysDictDataSearchDTO dto) {
        IPage page= PageDTO.of(dto.getPage(),dto.getSize());

        return this.baseMapper.findByDictTypePage(page,dto);
    }
}
